/*
 * Copyright 2003-2007 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

package com.colony.remote.rpc;

import java.io.IOException;

import com.argot.TypeException;
import com.argot.TypeInputStream;
import com.argot.TypeLibrary;
import com.argot.TypeOutputStream;
import com.argot.TypeReader;
import com.argot.TypeWriter;
import com.argot.common.BigEndianUnsignedByte;
import com.argot.common.BigEndianUnsignedShort;
import com.argot.common.U8Boolean;
import com.argot.meta.MetaAbstract;
import com.argot.remote.MetaParameter;
import com.argot.remote.MetaRemoteException;
import com.argot.remote.WrappedRemoteException;
import com.colony.remote.RemoteException;


public class RpcMetaResponse 
implements TypeReader, TypeWriter
{
	public static final String TYPENAME = "remote.rpc.response";
	
	public Object read(TypeInputStream in) 
	throws TypeException, IOException 
	{
		Boolean b = (Boolean) in.readObject( U8Boolean.TYPENAME );
		
		Short size = (Short) in.readObject( BigEndianUnsignedByte.TYPENAME );
		Object[] objs = new Object[ size.intValue() ];
		for ( int x=0; x<size.intValue(); x++ )
		{

			Integer id = (Integer) in.readObject( BigEndianUnsignedShort.TYPENAME );
			objs[x] = in.readObject( id.intValue() );
		}
		
		if ( b.booleanValue() )
			return new RpcMetaMethodRequest( (Throwable) objs[0], (Object[]) null );
		else
			return new RpcMetaMethodRequest( null, objs );
	}

	public void write(TypeOutputStream out, Object o) 
	throws TypeException, IOException 
	{
		RpcMetaMethodRequest request = (RpcMetaMethodRequest) o;
		
		Throwable exception = request.getException();
		if ( request.getException() != null )
		{
			TypeLibrary library = out.getTypeMap().getLibrary();
			out.writeObject( U8Boolean.TYPENAME, new Boolean( true ) );
			out.writeObject( BigEndianUnsignedByte.TYPENAME, new Integer(1));
			
			int id = -1;
			
			// Look through the possible return exceptions.  If it is
			// assignable and can be mapped, then return it.
			id = request.getMetaMethod().getMatchingErrorType(exception);
			if (id!=TypeLibrary.NOTYPE)
			{
               id = out.getTypeMap().getId( id );
			}
			
			if ( id == -1 )
			{
				MetaAbstract exAbstract = (MetaAbstract) library.getStructure(library.getId("remote.exception"));
				if (exAbstract.isMapped(id))
				{
					id = library.getId("remote.exception");
				}
			}
            
			if ( id == -1 )
			{
				// exception isn't mapped.  So return remote exception.
				RemoteException ex = new RemoteException( "Undeclared Exception", request.getException() );				
				out.writeObject( BigEndianUnsignedShort.TYPENAME, new Integer( out.getTypeMap().getId(RemoteException.TYPENAME) ) );				
				out.writeObject( WrappedRemoteException.TYPENAME, ex );
			}
			else
			{
				if ( MetaRemoteException.isWrapRequired(out.getTypeMap().getLibrary(), request.getException()))
				{
					RemoteException ex = new RemoteException( "Undeclared Exception", request.getException() );
					out.writeObject( BigEndianUnsignedShort.TYPENAME, new Integer( out.getTypeMap().getId(RemoteException.TYPENAME) ) );				
					out.writeObject( WrappedRemoteException.TYPENAME, ex );					
				} else {
					out.writeObject( BigEndianUnsignedShort.TYPENAME, new Integer( id ) );				
					out.writeObject( id, request.getException() );
				}
			}			
		}
		else
		{
			out.writeObject( U8Boolean.TYPENAME, new Boolean( false ) );
			
			MetaParameter[] responseTypes = request.getMetaMethod().getResponseTypes();
			Object[] args = request.getResponse();
			
			out.writeObject( BigEndianUnsignedByte.TYPENAME, new Integer( responseTypes.length ));
			for( int x=0; x< responseTypes.length; x++ )
			{
				int mappedId = out.getTypeMap().getId( responseTypes[x].getParamType() );
				out.writeObject( BigEndianUnsignedShort.TYPENAME, new Integer( mappedId ) );
				
				out.writeObject( mappedId, args[x] );
			}
			
		}
	}

}
